package org.wisdom.ac.apis.controller;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.criteria.*;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.StringUtils;
import org.wisdom.ac.apis.UserController;
import org.wisdom.ac.apis.assembler.UserAssembler;
import org.wisdom.ac.application.service.AuthService;
import org.wisdom.ac.domain.user.entity.AcUser;
import org.wisdom.ac.domain.user.entity.AcUserRole;
import org.wisdom.ac.domain.user.repository.UserRepository;
import org.wisdom.ac.dto.AuthUserDto;
import org.wisdom.ac.dto.UserDto;
import org.wisdom.annotation.WisdomController;
import org.wisdom.context.WisdomContextHolder;
import org.wisdom.exception.BusinessException;
import org.wisdom.vo.PageModel;
import org.wisdom.vo.ResponseResult;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 系统用户API
 *
 * @author 张智研
 * @date 2022/05/31 10:21
 **/
@Slf4j
@RequiredArgsConstructor
@WisdomController
public class UserControllerImpl implements UserController {

    private final UserRepository userRepository;
    private final UserAssembler userAssembler;
    private final PasswordEncoder passwordEncoder;
    private final AuthService authService;

    @Override
    public ResponseResult<UserDto> getInfo() {
        AcUser user = this.authService.getInfo();
        UserDto userDto = this.userAssembler.userEntityToDto(user);
        return ResponseResult.ok(userDto);
    }

    @Override
    public ResponseResult<String> save(UserDto userDto) {
        AcUser user = this.userAssembler.userDtoToEntity(userDto);
        if (StringUtils.hasLength(userDto.getPassword())) {
            user.setPassword(this.passwordEncoder.encode(userDto.getPassword()));
        }
        user = this.userRepository.save(user);
        return ResponseResult.ok(user.getId());
    }

    @Override
    public ResponseResult<UserDto> get(String userId) {
        AcUser user = this.userRepository.findById(userId).orElse(null);
        if (user == null) {
            throw new BusinessException("用户" + userId + "不存在！");
        }
        UserDto userDto = this.userAssembler.userEntityToDto(user);
        return ResponseResult.ok(userDto);
    }

    @Override
    public ResponseResult<String> delete(String userId) {
        this.userRepository.deleteById(userId);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult<PageModel<UserDto>> list(UserDto userDto) {
        Sort sort = Sort.unsorted();
        AcUser user = this.userAssembler.userDtoToEntity(userDto);
        Page<AcUser> userPage =
            this.userRepository.findAll(user, userDto.getPageNumber() - 1, userDto.getPageSize(), sort);
        return ResponseResult.ok(this.toPageModel(userPage));
    }

    @Override
    public ResponseResult<String> resetPwd(UserDto userDto) {
        boolean isPresent = this.userRepository.existsById(userDto.getId());
        if (isPresent) {
            String password = userDto.getPassword();
            this.userRepository.updatePassword(userDto.getId(),this.passwordEncoder.encode(password));
            return ResponseResult.ok();
        } else {
            return ResponseResult.fail("用户不存在！");
        }
    }

    @Override
    public ResponseResult<PageModel<UserDto>> allocatedList(UserDto userDto) {
        return this.getUsers(userDto, true);
    }

    @Override
    public ResponseResult<PageModel<UserDto>> unallocatedList(UserDto userDto) {
        return this.getUsers(userDto, false);
    }

    @Override
    public ResponseResult<String> authUser(AuthUserDto authUserDto) {
        this.authService.authUser(authUserDto);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult<String> authUserCancel(AuthUserDto authUserDto) {
        this.authService.authUserCancel(authUserDto);
        return ResponseResult.ok();
    }

    @Override
    public ResponseResult<String> updateUserPwd(UserDto userDto) {
        String userId = WisdomContextHolder.getContext().getUserId();
        AcUser user = this.userRepository.getById(userId);
        boolean b = this.passwordEncoder.matches(userDto.getOldPassword(),user.getPassword());
        if(b) {
            this.userRepository.updatePassword(userId,this.passwordEncoder.encode(userDto.getPassword()));
            return ResponseResult.ok("修改密码成功！");
        }else {
            return ResponseResult.fail("旧密码不正确！");
        }
    }

    /**
     * 获取用户
     *
     * @author 张智研
     * @date 2023/05/17 13:30
     * @param userDto
     *            查询条件
     * @param isAllocated
     *            是否分配
     * @return org.wisdom.vo.ResponseResult<org.wisdom.vo.PageModel<org.wisdom.ac.dto.UserDto>>
     */
    private ResponseResult<PageModel<UserDto>> getUsers(UserDto userDto, boolean isAllocated) {
        String[] userRoles = userDto.getUserRoles();
        if (userRoles.length != 1) {
            return ResponseResult.fail("角色查询条件必须唯一:" + userRoles.length);
        }
        Specification<AcUser> querySpec = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasLength(userDto.getUserName())) {
                predicates.add(
                    criteriaBuilder.like(root.get("userName").as(String.class), "%" + userDto.getUserName() + "%"));
            }
            if (StringUtils.hasLength(userDto.getAliasName())) {
                predicates.add(
                    criteriaBuilder.like(root.get("aliasName").as(String.class), "%" + userDto.getAliasName() + "%"));
            }
            if (userDto.getUserRoles() != null && userDto.getUserRoles().length > 0) {
                if (isAllocated) {
                    ListJoin<AcUser, AcUserRole> join =
                            root.join(root.getModel().getList("userRoles", AcUserRole.class), JoinType.LEFT);
                    predicates.add(criteriaBuilder.equal(join.get("roleId").as(String.class), userDto.getUserRoles()[0]));
                } else {
                    Subquery<AcUserRole> sq = criteriaQuery.subquery(AcUserRole.class);
                    Root<AcUserRole> root1 = sq.from(AcUserRole.class);
                    Predicate p1 = criteriaBuilder.equal(root1.get("userId"), root.get("id"));
                    Predicate p2 = criteriaBuilder.equal(root1.get("roleId"), userDto.getUserRoles()[0]);
                    sq.select(root1).where(criteriaBuilder.and(p1,p2));
                    predicates.add(criteriaBuilder.isNull(sq));
                }
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        if (userDto.getIsPage()) {
            Pageable pageable = PageRequest.of(userDto.getPageNumber() - 1, userDto.getPageSize(), Sort.unsorted());
            Page<AcUser> userPage = this.userRepository.findAll(querySpec, pageable);
            return ResponseResult.ok(this.toPageModel(userPage));
        } else {
            List<AcUser> userList = this.userRepository.findAll(querySpec);
            List<UserDto> userDtoList = this.userAssembler.userListEntityToDto(userList);
            PageModel<UserDto> pageModel = new PageModel<>();
            pageModel.setContent(userDtoList);
            pageModel.setTotalPages(userDtoList.size());
            pageModel.setTotalElements(userDtoList.size());
            pageModel.setPageNumber(1);
            pageModel.setPageSize(1);
            return ResponseResult.ok(pageModel);
        }
    }

    /**
     * 封装结果集
     *
     * @author 张智研
     * @date 2023/05/17 13:30
     * @param userPage
     *            用户分页结果
     * @return org.wisdom.vo.PageModel<org.wisdom.ac.dto.UserDto>
     */
    private PageModel<UserDto> toPageModel(Page<AcUser> userPage) {
        PageModel<UserDto> pageModel = new PageModel<>();
        List<UserDto> userDtoList = this.userAssembler.userListEntityToDto(userPage.getContent());
        pageModel.setContent(userDtoList);
        pageModel.setTotalPages(userPage.getTotalPages());
        pageModel.setTotalElements(userPage.getTotalElements());
        pageModel.setPageNumber(userPage.getNumber() + 1);
        pageModel.setPageSize(userPage.getSize());
        return pageModel;
    }
}
